﻿//////////////////////////////////////////////////////////////////////////////
//
// Copyright © 1998-2024 Glodon.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the “Software”),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//////////////////////////////////////////////////////////////////////////////

using System.ComponentModel;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Interop;

using Microsoft.Extensions.DependencyInjection;

#if Gdmp || Gnuf
using Glodon.Gdmp.UI;
#elif Gap
using Glodon.Gap.UI;
#endif
using Glodon.Lookup.Core.Contracts;
using Glodon.Lookup.Core.Enums;
using Glodon.Lookup.Core.ModelBase;
using Glodon.Lookup.Lookup.Core;
using Glodon.Lookup.Models;
using Glodon.Lookup.Services.Contracts;
using Glodon.Lookup.ViewModels.Contracts;
using Glodon.Lookup.ViewModels.Objects;
using Glodon.Lookup.ViewModels.Pages.Tracebale;
using Glodon.Lookup.Views.Extensions;
using Glodon.Lookup.Views.Utils;

using Wpf.Ui.Appearance;
using Wpf.Ui.Common;
using Wpf.Ui.Contracts;
using Wpf.Ui.Controls;

using CommunityToolkit.Mvvm.Input;

using Glodon.Lookup.Utils;

namespace Glodon.Lookup.Views.Pages
{
    /// <summary>
    /// Interaction logic for GLookupWindow.xaml
    /// </summary>
    public partial class GLookupWindow : Window, IWindow
    {
        private int _scrollTick;
        public ITabGroupViewModel<SnoopHistoryViewModel> TabGroupViewModel { get; set; }

        private readonly IServiceScope _serviceScope;
        public IServiceProvider Scope => _serviceScope.ServiceProvider;

        public GLookupWindow(IServiceScopeFactory scopeFactory, ISettingsService settingsService)
        {
            Wpf.Ui.Application.MainWindow = this;
#if Gnuf
            if (GlodonAPI.UiApplication.UITheme == UITheme.Dark)
            {
                settingsService.Theme = ThemeType.Dark;
            }
            else
            {
                settingsService.Theme = ThemeType.Light;
            }
#endif
            Theme.Apply(this, settingsService.Theme, settingsService.Background);


            InitializeComponent();
            //This line is used to avoid encountering a strange is sue of XamlBehaviorsWpf,
            //see https://github.com/microsoft/XamlBehaviorsWpf/issues/86 to find more
            _ = new Microsoft.Xaml.Behaviors.DefaultTriggerAttribute(typeof(Trigger), typeof(Microsoft.Xaml.Behaviors.TriggerBase), null);

            _serviceScope = scopeFactory.CreateScope();
            TabGroupViewModel = Scope.GetService<ITabGroupViewModel<SnoopHistoryViewModel>>()!;
            DataContext = TabGroupViewModel;

            var windowController = Scope.GetService<IWindowController>();
            var snackbarService = Scope.GetService<ISnackbarService>();


            (TabGroupViewModel as ISnoopViewModel)!.ExitCommand = new ButtonCommand(() =>
            {
                var apiEventHandler = TabGroupViewModel as IApiEventHandler;
                TabGroupViewModel!.ClearData();
                GlodonAPI.DeregistHandlers(apiEventHandler!);
                Close();
            });

            windowController!.SetControlledWindow(this);
            snackbarService!.SetSnackbarControl(RootSnackbar);
            snackbarService.Timeout = 3000;

            Closing += ClosingWindow;
            Activated += (sender, _) => Wpf.Ui.Application.MainWindow = (Window)sender;
            WindowStartupLocation = WindowStartupLocation.CenterScreen;

            new WindowInteropHelper(this).Owner = (IntPtr)UIApplication.Get().GetApplicaitonWindowId();
        }
        public void OnGridLoaded(object sender, RoutedEventArgs e)
        {
            try
            {
                if (TabGroupViewModel.CurrentViewModel is { SnoopableObjects: { Count: > 0 } })
                {
                    if (TabGroupViewModel.CurrentViewModel.SnoopableObjects.Any(sn => sn.IsSelected))
                    {
                        _ = (TabGroupViewModel as ISnoopViewModel)!.FetchMembersCommand.ExecuteAsync(null);
                    }
                }
            }
            catch (Exception ex)
            {
                Scope.GetService<ISnackbarService>()!.GetSnackbarControl().ShowAsync("表格加载捕获异常", ex.Message + ex.StackTrace,
                    SymbolRegular.ErrorCircle16, ControlAppearance.Danger);
            }
        }
        private void ClosingWindow(object sender, CancelEventArgs e)
        {
            try
            {
                GlodonAPI.DeregistHandlers((TabGroupViewModel as IApiEventHandler)!);
                TabGroupViewModel.ClearData();
                Hide();
                e.Cancel = true;
            }
            catch (Exception ex)
            {
                ExceptionUtils.ShowExceptionMessage(ex);
            }
        }
        private void UnloadServices(object sender, RoutedEventArgs e)
        {
            _serviceScope.Dispose();
        }
        protected void OnGridMouseLeftButtonUp(object sender, RoutedEventArgs routedEventArgs)
        {
            if (sender is DependencyObject dp)
            {
                var datagrid = VisualUtils.FindVisualParent<System.Windows.Controls.DataGrid>(dp);
                if (datagrid == null)
                {
                    Scope.GetService<ISnackbarService>()!.ShowAsync("未找到DataGrid控件", $"位于方法{nameof(OnGridMouseLeftButtonUp)}",
                        Wpf.Ui.Common.SymbolRegular.ErrorCircle12, Wpf.Ui.Controls.ControlAppearance.Danger);
                    return;
                }
                if (datagrid!.SelectedItems.Count != 1) return;
                (TabGroupViewModel as ISnoopViewModel)!.Navigate((Descriptor)datagrid.SelectedItem);
            }
        }


        protected void OnDataGridScrollChanged(object sender, ScrollChangedEventArgs e)
        {
            if (e.VerticalChange != 0) _scrollTick = Environment.TickCount;
        }


        private void OnGridToolTipOpening(object o, ToolTipEventArgs args)
        {
            //Fixed by the tooltip work in 6.0-preview7 https://github.com/dotnet/wpf/pull/6058 but we use net48

            if (_scrollTick != 0 && Environment.TickCount - _scrollTick < 73) args.Handled = true;
        }


        protected async void OnRowLoaded(object sender, RoutedEventArgs routedEventArgs)
        {
            //Lazy init. 1 ms is enough to display data and start initialising components
            await Task.Delay(1);

            var element = (FrameworkElement)sender;
            Descriptor descriptor;
            switch (element.DataContext)
            {
                case SnoopableObject context:
                    descriptor = context.Descriptor;
                    var treeItem = VisualUtils.FindVisualParent<System.Windows.Controls.TreeViewItem>((DependencyObject)sender);
                    CreateTreeTooltip(descriptor, treeItem);
                    CreateTreeContextMenu(descriptor, treeItem);
                    break;
                case Descriptor context:
                    descriptor = context;
                    CreateGridRowContextMenu(descriptor, element);
                    break;
                default:
                    return;
            }
        }

        private void CreateTreeTooltip(Descriptor descriptor, FrameworkElement row)
        {
            row.ToolTip = new ToolTip
            {
                Content = new StringBuilder()
                    .Append("Type: ")
                    .AppendLine(descriptor.Type)
                    .Append("Value: ")
                    .Append(descriptor.Name)
                    .ToString()
            };

            row.ToolTipOpening += OnGridToolTipOpening;
        }

        private void CreateGridTooltip(Descriptor descriptor, FrameworkElement row)
        {
            string toolTipTitle;
            switch (descriptor.MemberAttributes)
            {
                case MemberAttributes.Property:
                    toolTipTitle = "Property: ";
                    break;
                case MemberAttributes.Extension:
                    toolTipTitle = "Extension: ";
                    break;
                default:
                    toolTipTitle = "Method: ";
                    break;
            }
            var builder = new StringBuilder()
                .Append(toolTipTitle)
                .AppendLine(descriptor.Name)
                .Append("Type: ")
                .AppendLine(descriptor.Value.Descriptor.Type)
                .Append("Value: ")
                .Append(descriptor.Value.Descriptor.Name);

            if (!(descriptor.Value.Descriptor.Description is null))
                builder.AppendLine()
                    .Append("Description: ")
                    .Append(descriptor.Value.Descriptor.Description);

            row.ToolTip = new ToolTip
            {
                Content = builder.ToString()
            };

            row.ToolTipOpening += OnGridToolTipOpening;
        }

        private void CreateTreeContextMenu(Descriptor descriptor, FrameworkElement row)
        {
            var contextMenu = new ContextMenu
            {
                Resources = Resources
            };

            contextMenu.AddMenuItem("CopyMenuItem")
                .SetHeader("复制")
                .SetCommand(descriptor, parameter => Clipboard.SetText(parameter.Name));

            if (descriptor is IDescriptorConnector connector)
                connector.RegisterMenu(contextMenu, row);
            row.ContextMenu = contextMenu;
        }

        private void CreateGridRowContextMenu(Descriptor descriptor, FrameworkElement row)
        {
            var contextMenu = new ContextMenu
            {
                Resources = Resources
            };

            contextMenu.AddMenuItem("CopyMenuItem")
                .SetHeader("复制")
                .SetCommand(descriptor, parameter => Clipboard.SetText($"{parameter.Name}: {parameter.Value.Descriptor.Name}"));

            contextMenu.AddMenuItem("CopyMenuItem")
                .SetHeader("复制值")
                .SetCommand(descriptor, parameter => Clipboard.SetText(parameter.Value.Descriptor.Name))
                .SetAvailability(descriptor.Value.Descriptor.Name is not null);


            if (descriptor.Value.Descriptor is IDescriptorConnector connector)
                connector.RegisterMenu(contextMenu, row);
            row.ContextMenu = contextMenu;
        }

        private void AddShortcuts()
        {
            var command = new RelayCommand(() => { (TabGroupViewModel as ISnoopViewModel)!.RefreshMembersCommand.Execute(null); });
            InputBindings.Add(new KeyBinding(command, new KeyGesture(Key.F5)));
        }

        public void Show(Window window)
        {
            Left = window.Left + 47;
            Top = window.Top + 49;
            Show();
        }

        public void ShowAttached()
        {
            WindowStartupLocation = WindowStartupLocation.CenterScreen;
            Show();
        }

        public void Initialize()
        {
            throw new NotImplementedException();
        }

        private void Grid_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            System.Windows.Controls.TreeViewItem groupTreeViewItem = VisualUtils.FindVisualParent<System.Windows.Controls.TreeViewItem>(sender as DependencyObject);
            groupTreeViewItem.IsExpanded = !groupTreeViewItem.IsExpanded;
        }

    }
}
